/**
 * 
 */
package com.googlecode.wmlynar.gwtbeans.xml;

import java.util.ArrayList;
import java.util.Stack;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;

import com.googlecode.wmlynar.gwtbeans.gen.Binding;
import com.googlecode.wmlynar.gwtbeans.gen.Field;
import com.googlecode.wmlynar.gwtbeans.gen.Mapping;
import com.googlecode.wmlynar.gwtbeans.gen.Mappings;

/**
 * Class managing the parsing of the mappings xml file.
 * 
 * @author Wojciech Mlynarczyk (wmlynar@gmail.com)
 * 
 */
public class MappingsContentHandler implements ContentHandler {

    public enum ITEM_TYPE {
        ITEM_NONE, ITEM_MAPPINGS, ITEM_MAPPING, ITEM_BINDING, ITEM_CLASS_A, ITEM_CLASS_B, ITEM_FIELD, ITEM_A, ITEM_B, ITEM_TOA, ITEM_TOB, ITEM_IMMUTABLE
    }

    private ITEM_TYPE itemType = ITEM_TYPE.ITEM_NONE;

    private ArrayList<Mapping> mappingList;
    private Mapping mapping;
    private ArrayList<Binding> bindingList;
    private Binding binding;
    private ArrayList<Field> fields;
    private Field field;
    private String classAName = null;
    private String classBName = null;
    private final Mappings mappings = new Mappings();
    private String text = "";

    Stack<ITEM_TYPE> stack = new Stack<ITEM_TYPE>();

    public ArrayList<Mapping> getMappingList() {
        return mappingList;
    }

    public ArrayList<Binding> getBindingList() {
        return bindingList;
    }

    public Mappings getMappings() {
        return mappings;
    }

    public void startElement(final String uri, final String localName, final String qName,
            final Attributes atts) throws SAXException {
        stack.push(itemType);
        if ("mappings".equalsIgnoreCase(localName)) {
            itemType = ITEM_TYPE.ITEM_MAPPINGS;
            mappingList = new ArrayList<Mapping>();
            bindingList = new ArrayList<Binding>();
            mappings.setMappingList(mappingList);
            mappings.setBindingList(bindingList);
            mappings.setImmutableList(new ArrayList<String>());
        } else if ("mapping".equalsIgnoreCase(localName)) {
            itemType = ITEM_TYPE.ITEM_MAPPING;
            mapping = new Mapping();
            mappingList.add(mapping);
            fields = new ArrayList<Field>();
            mapping.setFields(fields);
            for (int i = 0; i < atts.getLength(); i++) {
                if ("name".equalsIgnoreCase(atts.getLocalName(i))) {
                    mapping.setName(atts.getValue(i));
                } else if ("automapping".equalsIgnoreCase(atts.getLocalName(i))) {
                    mapping.setAutoMapping("true".equals(atts.getValue(i)));
                }
            }
            classAName = null;
            classBName = null;
        } else if ("binding".equalsIgnoreCase(localName)) {
            itemType = ITEM_TYPE.ITEM_BINDING;
            binding = new Binding();
            bindingList.add(binding);
            fields = new ArrayList<Field>();
            binding.setFields(fields);
            for (int i = 0; i < atts.getLength(); i++) {
                if ("name".equalsIgnoreCase(atts.getLocalName(i))) {
                    binding.setName(atts.getValue(i));
                }
            }
            classAName = null;
            classBName = null;
        } else if ("class-a".equalsIgnoreCase(localName)) {
            itemType = ITEM_TYPE.ITEM_CLASS_A;
            text = "";
        } else if ("class-b".equalsIgnoreCase(localName)) {
            itemType = ITEM_TYPE.ITEM_CLASS_B;
            text = "";
        } else if ("field".equalsIgnoreCase(localName)) {
            itemType = ITEM_TYPE.ITEM_FIELD;
            field = new Field();
            fields.add(field);
        } else if ("a".equalsIgnoreCase(localName)) {
            itemType = ITEM_TYPE.ITEM_A;
            text = "";
        } else if ("b".equalsIgnoreCase(localName)) {
            itemType = ITEM_TYPE.ITEM_B;
            text = "";
        } else if ("toa".equalsIgnoreCase(localName)) {
            itemType = ITEM_TYPE.ITEM_TOA;
            text = "";
        } else if ("tob".equalsIgnoreCase(localName)) {
            itemType = ITEM_TYPE.ITEM_TOB;
            text = "";
        } else if ("immutable".equalsIgnoreCase(localName)) {
            itemType = ITEM_TYPE.ITEM_IMMUTABLE;
            text = "";
        } else {
            throw new SAXException("Unknown element " + localName);
        }
    }

    public void endElement(final String uri, final String localName, final String qName)
            throws SAXException {
        if ("mapping".equalsIgnoreCase(localName)) {
            mapping.setClassAName(classAName);
            mapping.setClassBName(classBName);
            mapping = null;
        } else if ("binding".equalsIgnoreCase(localName)) {
            binding.setClassAName(classAName);
            binding.setClassBName(classBName);
            binding = null;
        } else if ("field".equalsIgnoreCase(localName)) {
            field = null;
        }

        final String str = text.trim();
        if (itemType == ITEM_TYPE.ITEM_CLASS_A) {
            classAName = str;
        } else if (itemType == ITEM_TYPE.ITEM_CLASS_B) {
            classBName = str;
        } else if (itemType == ITEM_TYPE.ITEM_A) {
            field.setFieldAName(str);
        } else if (itemType == ITEM_TYPE.ITEM_B) {
            field.setFieldBName(str);
        } else if (itemType == ITEM_TYPE.ITEM_TOA) {
            field.setToAConverterName(str);
        } else if (itemType == ITEM_TYPE.ITEM_TOB) {
            field.setToBConverterName(str);
        } else if (itemType == ITEM_TYPE.ITEM_IMMUTABLE) {
            mappings.getImmutableList().add(str);
        }

        itemType = stack.pop();
    }

    public void characters(final char[] ch, final int start, final int length) throws SAXException {
        text = text + new String(ch, start, length);
    }

    public void endDocument() throws SAXException {
    }

    public void endPrefixMapping(final String arg0) throws SAXException {
    }

    public void ignorableWhitespace(final char[] arg0, final int arg1, final int arg2)
            throws SAXException {
    }

    public void processingInstruction(final String arg0, final String arg1) throws SAXException {
    }

    public void setDocumentLocator(final Locator arg0) {
    }

    public void skippedEntity(final String arg0) throws SAXException {
    }

    public void startDocument() throws SAXException {
    }

    public void startPrefixMapping(final String arg0, final String arg1) throws SAXException {
    }
}
